package cn.wostore.wxhelper.utils;

import android.Manifest;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import cn.wostore.wxhelper.WxHelperApp;
import io.reactivex.annotations.NonNull;
import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteDatabaseHook;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.security.MessageDigest;
import java.util.List;

import static android.content.Context.TELEPHONY_SERVICE;

/**
 * @author wuch
 * @date 2018/12/25 0025
 */

public class WxDatabaseWrapper {
    private static final String TAG = WxDatabaseWrapper.class.getSimpleName();
    private static final String WX_ROOT_PATH = "/data/data/com.tencent.mm/";
    private static final String WX_SP_UIN_PATH = WX_ROOT_PATH + "shared_prefs/auth_info_key_prefs.xml";
    private static final String WX_DB_FILE_NAME = "EnMicroMsg.db";
    private String dbInfoFilePath = "/mnt/sdcard/wxhelper/db.txt";

    private static WxDatabaseWrapper INSTANCE;

    private SQLiteDatabase database;

    private WxDatabaseWrapper(){
    }

    public static WxDatabaseWrapper getInstance(){
        if (INSTANCE == null){
            synchronized (WxDatabaseWrapper.class){
                if (INSTANCE == null){
                    INSTANCE = new WxDatabaseWrapper();
                }
            }
        }
        return INSTANCE;
    }

    /**
     * 初始化或同步微信数据库
     */
    public synchronized void sync(){
        restoreWxDb();
        L.d(TAG, "sync weixin database");
        boolean execResult = execRootCmd("chmod -R 777 " + WX_ROOT_PATH);
        if (!execResult) return;
        String wxPath = TxtUtil.readFile(dbInfoFilePath);
        String newPath = WxHelperApp.getInstance().getFilesDir().getAbsolutePath() + File.separator + WX_DB_FILE_NAME;
        copyFile(wxPath, newPath);
        String pwd = getDbPassword(getPhoneIMEI(), getWxUin());
        database = openDbFile(newPath, pwd);
    }

    public SQLiteDatabase getDatabase() {
        if (database == null){
            sync();
        }
        return database;
    }


    /**
     * 以root权限执行linux指令
     *
     * @param paramString
     */
    public boolean execRootCmd(String paramString) {
        Process process = null;
        DataOutputStream os = null;
        try {
            process = Runtime.getRuntime().exec("su");
            os = new DataOutputStream(process.getOutputStream());
            os.writeBytes(paramString + "\n");
            os.writeBytes("exit\n");
            os.flush();
            process.waitFor();
        } catch (Exception e) {
            L.e(TAG, "执行命令失败："+ e.getMessage());
            return false;
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
                if (process != null) {
                    process.destroy();
                }
            } catch (Exception e) {
                L.e(TAG, "执行命令失败："+ e.getMessage());
            }
        }
        return true;
    }

    /**
     * 获取手机的imei码
     */
    private String getPhoneIMEI() {
        TelephonyManager tm = (TelephonyManager)WxHelperApp.getInstance().getSystemService(TELEPHONY_SERVICE);
        if (ActivityCompat.checkSelfPermission(WxHelperApp.getInstance(), Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            return "";
        }
        String IMEI = tm.getDeviceId();
        L.d(TAG, "get IMEI: "+IMEI);
        return IMEI;
    }

    /**
     * 获取微信的uid
     * 微信的uid存储在SharedPreferences里面
     * 存储位置\data\data\com.tencent.mm\shared_prefs\auth_info_key_prefs.xml
     */
    private String getWxUin() {
        String wxUin = "";
        L.d(TAG, String.valueOf(execRootCmd("chmod 777 " + WX_SP_UIN_PATH)));
        File file = new File(WX_SP_UIN_PATH);
        try {
            FileInputStream in = new FileInputStream(file);
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(in);
            Element root = document.getRootElement();
            List<Element> elements = root.elements();
            for (Element element : elements) {
                if ("_auth_uin".equals(element.attributeValue("name"))) {
                    wxUin = element.attributeValue("value");
                }
            }
        } catch (Exception e) {
            L.e(TAG, "获取微信uid失败，请检查auth_info_key_prefs文件权限: "+e.getMessage());
        }
        L.d(TAG, "get wxUin: "+wxUin);
        return wxUin;
    }

    /**
     * 根据imei和uin生成的md5码，获取数据库的密码（去前七位的小写字母）
     */
    private String getDbPassword(String imei, String uin) {
        String dbPassword;
        if (TextUtils.isEmpty(imei) || TextUtils.isEmpty(uin)) {
            L.d(TAG, "初始化数据库密码失败：imei或uid为空");
            return null;
        }
        String md5 = md5(imei + uin);
        if (TextUtils.isEmpty(md5)){
            return null;
        }
        dbPassword = md5.substring(0, 7).toLowerCase();
        saveDbPassword(dbPassword);
        L.d(TAG, "get dbPassword: "+dbPassword);
        return dbPassword;
    }

    private void saveDbPassword(String dbPassword){
        SharedPreferences sPreferences = WxHelperApp.getInstance().getSharedPreferences("cn.wostore.wxhelper_preferences", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sPreferences.edit();
        editor.putString("dbPassword", dbPassword);
        editor.apply();
    }

    /**
     * md5加密
     */
    private String md5(String content) {
        MessageDigest md5;
        try {
            md5 = MessageDigest.getInstance("MD5");
            md5.update(content.getBytes("UTF-8"));
            byte[] encryption = md5.digest();//加密
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < encryption.length; i++) {
                if (Integer.toHexString(0xff & encryption[i]).length() == 1) {
                    sb.append("0").append(Integer.toHexString(0xff & encryption[i]));
                } else {
                    sb.append(Integer.toHexString(0xff & encryption[i]));
                }
            }
            return sb.toString();
        } catch (Exception e) {
            L.e(TAG, e.getMessage());
            return null;
        }
    }

    /**
     * 复制文件
     *
     */
    public void copyFile(String oldPath, String newPath) {
        try {
            int byteRead;
            File oldFile = new File(oldPath);
            File newFile = new File(newPath);
            if (oldFile.exists()) {
                if (newFile.exists()){
                    newFile.delete();
                }
                InputStream inStream = new FileInputStream(oldPath);
                FileOutputStream fs = new FileOutputStream(newPath);
                byte[] buffer = new byte[1024];
                while ((byteRead = inStream.read(buffer)) != -1) {
                    fs.write(buffer, 0, byteRead);
                }
                inStream.close();
                L.d(TAG, "copy "+oldPath+" to "+newPath+" succeed!");
            } else {
                L.e(TAG, oldPath + "does NOT exist!");
            }
        } catch (Exception e) {
            L.e(TAG, "FAIL copy "+oldPath+" to "+newPath+e.getMessage());
        }
    }

    private SQLiteDatabase openDbFile(@NonNull String path, @NonNull String password) {
        SQLiteDatabase.loadLibs(WxHelperApp.getInstance());
        SQLiteDatabaseHook hook = new SQLiteDatabaseHook() {
            public void preKey(SQLiteDatabase database) {
            }

            public void postKey(SQLiteDatabase database) {
                database.rawExecSQL("PRAGMA cipher_migrate;"); //兼容2.0的数据库
            }
        };
        return SQLiteDatabase.openOrCreateDatabase(path, password, null, hook);
    }

    /**
     * 解决程序异常关闭之后再次打开会导致数据库异常
     */
    private void restoreWxDb(){
        try {
            File dbShm = new File(WxHelperApp.getInstance().getFilesDir().getAbsolutePath() + File.separator + "EnMicroMsg.db-shm");
            File dbWal = new File(WxHelperApp.getInstance().getFilesDir().getAbsolutePath() + File.separator + "EnMicroMsg.db-wal");
            if (dbShm.exists() || dbWal.exists()){
                L.d(TAG, "application does NOT close normally last time");
            }
            if (dbShm.exists()){
                dbShm.delete();
            }
            if (dbWal.exists()){
                dbWal.delete();
            }
            L.d(TAG, "restore database success!");
        } catch (Exception e) {
            L.e(TAG, "restore database fail! "+e.getMessage());
        }
    }

}
